package com.ctvit.solr.kit.solr.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import cms.api.common.constant.SolrApiConstant;
import cms.api.common.utils.SolrClientUtils;
import com.ctvit.solr.kit.cp.DocumentCondition;
import com.ctvit.solr.kit.cp.DocumentParam;
import com.ctvit.solr.kit.cp.Highlight;
import com.ctvit.solr.kit.cp.Simple;
import com.ctvit.solr.kit.solr.service.SolrService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.HighlightParams;
import org.apache.solr.common.util.NamedList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * Solr业务实现类
 *
 * @author heyingcheng
 * @email heyingcheng@ctvit.com.cn
 * @date 2019/6/23 20:22
 */
@Slf4j
@Service
public class SolrServiceImpl implements SolrService {

    @Autowired
    private SolrClient solrClient;

    @Override
    public JSONObject saveOrUpdateDocument(String collection, DocumentParam documentParam) {
        log.info("SolrServiceImpl.saveOrUpdateDocument, documentParam={}", JSON.toJSONString(documentParam));
        try {
            List<SolrInputDocument> documents = new ArrayList<>();
            List<JSONObject> docs = documentParam.getDocs();
            if (CollectionUtils.isNotEmpty(docs)) {
                // 创建文档
                docs.forEach(jsonObject -> {
                    SolrInputDocument document = new SolrInputDocument();
                    Set<Map.Entry<String, Object>> entries = jsonObject.entrySet();
                    entries.forEach(entry -> {
                        document.setField(entry.getKey(), entry.getValue());
                    });
                    documents.add(document);
                });

                // 一次性全部添加
                UpdateResponse updateResponse = solrClient.add(collection, documents);
                solrClient.commit(collection);
                JSONObject responseHeaderObject = SolrClientUtils.formatNamedList(updateResponse.getResponseHeader());
                JSONObject result = new JSONObject();
                result.put("responseHeader", responseHeaderObject);
                return result;
            }
        } catch (SolrServerException e) {
            log.error("添加或更新文档异常, SolrServerException, documentParam={}", JSON.toJSONString(documentParam), e);
        } catch (IOException e) {
            log.error("添加或更新文档异常, IOException, documentParam={}", JSON.toJSONString(documentParam), e);
        }
        return null;
    }

    @Override
    public JSONObject selectDocument(String collection, DocumentCondition documentCondition) {
        log.info("SolrServiceImpl.queryDocument, documentCondition={}", JSON.toJSONString(documentCondition));

        SolrQuery solrQuery = new SolrQuery(SolrApiConstant.DEFAULT_QUERY);
        // 设置查询条件
        setQuery(documentCondition, solrQuery);
        // 添加字段集
        addFields(documentCondition, solrQuery);
        // 添加过滤条件
        addFilterQuery(documentCondition, solrQuery);
        // 设置高亮
        setHightlight(documentCondition, solrQuery);
        // 添加排序
        addSort(documentCondition, solrQuery);
        // 设置分页, start开始, 每页rows条文档
        solrQuery.setStart(documentCondition.getStart()).setRows(documentCondition.getRows());

        QueryResponse queryResponse = null;
        JSONObject responseHeaderObject = null;
        JSONObject debugMapObject = null;
        try {
            // 执行查询
            log.info("SolrQuery.params={}", solrQuery.toString());
            queryResponse = solrClient.query(collection, solrQuery);
            NamedList responseHeader = queryResponse.getResponseHeader();
            responseHeaderObject = SolrClientUtils.formatNamedList(responseHeader);
            Map<String, Object> debugMap = queryResponse.getDebugMap();
            debugMapObject = SolrClientUtils.formatSolrMap(debugMap);
        } catch (SolrServerException e) {
            log.error("查询文档异常, SolrServerException, documentCondition={}", JSON.toJSONString(documentCondition), e);
            JSONObject result = new JSONObject();
            result.put(SolrApiConstant.ERROR, "查询文档异常");
            return result;
        } catch (IOException e) {
            log.error("查询文档异常, IOException, documentCondition={}", JSON.toJSONString(documentCondition), e);
            JSONObject result = new JSONObject();
            result.put(SolrApiConstant.ERROR, "查询文档异常");
            return result;
        }

        // 文档结果集
        SolrDocumentList docs = queryResponse.getResults();
        // 高亮显示的返回结果
        Map<String, Map<String, List<String>>> highlightMap = queryResponse.getHighlighting();

        List<Map<String, Object>> documents = new ArrayList();
        // 处理返回结果
        for (SolrDocument solrDocument : docs) {
            Map<String, Object> documentMap = new HashMap<>(16);
            {
                Set<Map.Entry<String, Object>> entries = solrDocument.entrySet();
                entries.forEach(entry -> {
                    documentMap.put(entry.getKey(), solrDocument.getFirstValue(entry.getKey()));
                });
            }
            log.info("documentMap={}", JSON.toJSONString(documentMap));

            {
                if (MapUtils.isNotEmpty(highlightMap)) {
                    Map<String, List<String>> fieldMap = highlightMap.get(solrDocument.get(SolrApiConstant.ID));
                    if (MapUtils.isNotEmpty(fieldMap)) {
                        log.info("fieldMap={}", JSON.toJSONString(fieldMap));
                        Set<Map.Entry<String, List<String>>> entries = fieldMap.entrySet();
                        entries.forEach(entry -> {
                            documentMap.put(entry.getKey(), fieldMap.get(entry.getKey()).get(0));
                        });
                    }
                }
            }

            documents.add(documentMap);
        }

        JSONObject result = new JSONObject();
        result.put("responseHeader", responseHeaderObject);
        result.put("debug", debugMapObject);
        JSONObject r = new JSONObject();
        r.put("start", solrQuery.getStart());
        r.put("numFound", docs.getNumFound());
        r.put("docs", documents);
        result.put("response", r);

        return result;
    }

    @Override
    public JSONObject deleteById(String collection, String id) {
        log.info("SolrServiceImpl.deleteById, collection=[{}], id=[{}]", collection, id);
        return deleteByIds(collection, Arrays.asList(new String[]{id}));
    }

    @Override
    public JSONObject deleteByIds(String collection, List<String> ids) {
        log.info("SolrServiceImpl.deleteByIds, collection=[{}], ids=[{}]", collection, ids);
        try {
            UpdateResponse updateResponse = solrClient.deleteById(collection, ids);
            solrClient.commit(collection);
            JSONObject responseHeaderObject = SolrClientUtils.formatNamedList(updateResponse.getResponseHeader());
            JSONObject result = new JSONObject();
            result.put("responseHeader", responseHeaderObject);
            return result;
        } catch (SolrServerException e) {
            log.error("按ID批量删除文档异常, SolrServerException, collection=[{}], ids=[{}]", collection, ids, e);
        } catch (IOException e) {
            log.error("按ID批量删除文档异常, IOException, collection=[{}], ids=[{}]", collection, ids, e);
        }
        return null;
    }

    @Override
    public JSONObject deleteAll(String collection) {
        log.info("SolrServiceImpl.deleteAll, collection=[{}]", collection);
        try {
            UpdateResponse updateResponse = solrClient.deleteByQuery(collection, SolrApiConstant.DEFAULT_QUERY);
            solrClient.commit(collection);
            JSONObject responseHeaderObject = SolrClientUtils.formatNamedList(updateResponse.getResponseHeader());
            JSONObject result = new JSONObject();
            result.put("responseHeader", responseHeaderObject);
            return result;
        } catch (SolrServerException e) {
            log.error("删除全部文档异常, SolrServerException, collection=[{}]", collection, e);
        } catch (IOException e) {
            log.error("删除全部文档异常, IOException, collection=[{}]", collection, e);
        }
        return null;
    }

    /**
     * 设置查询条件
     *
     * @param documentCondition 文档查询条件
     * @param solrQuery         Solr查询对象
     * @return void
     * @author heyingcheng
     * @date 2019/6/23 22:08
     */
    private void setQuery(DocumentCondition documentCondition, SolrQuery solrQuery) {
        Set<String> querySet = new HashSet<>();
        List<JSONObject> queries = documentCondition.getQ();
        if (CollectionUtils.isNotEmpty(queries)) {
            queries.forEach(jsonObject -> {
                Set<String> innerSet = new HashSet<>();
                Set<Map.Entry<String, Object>> entries = jsonObject.entrySet();
                entries.forEach(entry -> {
                    innerSet.add(entry.getKey().concat(SolrApiConstant.COLON).concat(String.valueOf(entry.getValue())));
                });
                querySet.add(SolrApiConstant.LEFT_PARENTHESES.concat(StringUtils.join(innerSet, SolrApiConstant.AND)).concat(SolrApiConstant.RIGHT_PARENTHESES));
            });
            String query = StringUtils.join(querySet, SolrApiConstant.OR);
            log.info("SolrQuery.query={}", query);
            solrQuery.setQuery(query);
        }

        if (Objects.nonNull(documentCondition.getDebugQuery())) {
            solrQuery.setShowDebugInfo(documentCondition.getDebugQuery());
        }
    }

    /**
     * 添加字段集
     *
     * @param documentCondition 文档查询条件
     * @param solrQuery         Solr查询对象
     * @return void
     * @author heyingcheng
     * @date 2019/6/23 22:09
     */
    private void addFields(DocumentCondition documentCondition, SolrQuery solrQuery) {
        List<String> fields = documentCondition.getFl();
        if (CollectionUtils.isNotEmpty(fields)) {
            fields.forEach(field -> solrQuery.addField(field));
        }
    }

    /**
     * 添加过滤条件
     *
     * @param documentCondition 文档查询条件
     * @param solrQuery         Solr查询对象
     * @return void
     * @author heyingcheng
     * @date 2019/6/23 22:11
     */
    private void addFilterQuery(DocumentCondition documentCondition, SolrQuery solrQuery) {
        List<JSONObject> filterQueries = documentCondition.getFq();
        if (CollectionUtils.isNotEmpty(filterQueries)) {
            filterQueries.forEach(jsonObject -> {
                Set<Map.Entry<String, Object>> entries = jsonObject.entrySet();
                entries.forEach(entry -> {
                    solrQuery.addFilterQuery(entry.getKey().concat(SolrApiConstant.COLON).concat(String.valueOf(entry.getValue())));
                });
            });
        }
    }

    /**
     * 设置高亮
     *
     * @param documentCondition 文档查询条件
     * @param solrQuery         Solr查询对象
     * @return void
     * @author heyingcheng
     * @date 2019/6/23 22:12
     */
    private void setHightlight(DocumentCondition documentCondition, SolrQuery solrQuery) {
        Highlight highlight = documentCondition.getHl();
        if (Objects.nonNull(highlight)) {
            solrQuery.setHighlight(true);
            // 添加高亮字段，多个字段之间逗号隔开比如: A,B,C
            solrQuery.addHighlightField(StringUtils.join(highlight.getFl(), SolrApiConstant.COMMA));
            Simple simple = highlight.getSimple();
            if (Objects.nonNull(simple)) {
                // 设置高亮字段的前缀
                solrQuery.setHighlightSimplePre(simple.getPre());
                // 设置高亮字段的后缀
                solrQuery.setHighlightSimplePost(simple.getPost());
            }

            if (Objects.nonNull(highlight.getRequireFieldMatch())) {
                solrQuery.setHighlightRequireFieldMatch(highlight.getRequireFieldMatch().booleanValue());
            }

            if (Objects.nonNull(highlight.getUsePhraseHighlighter())) {
                solrQuery.set(HighlightParams.USE_PHRASE_HIGHLIGHTER, highlight.getUsePhraseHighlighter().booleanValue());
            }

            if (Objects.nonNull(highlight.getHighlightMultiTerm())) {
                solrQuery.set(HighlightParams.HIGHLIGHT_MULTI_TERM, highlight.getHighlightMultiTerm().booleanValue());
            }
        }
    }

    /**
     * 添加排序, 使用数值来指定排序的方式, 其中大于0为用于升序排列, 而小于0是用于降序排列
     *
     * @param documentCondition 文档查询条件
     * @param solrQuery         Solr查询对象
     * @return void
     * @author heyingcheng
     * @date 2019/6/23 22:13
     */
    private void addSort(DocumentCondition documentCondition, SolrQuery solrQuery) {
        JSONObject sort = documentCondition.getSort();
        if (Objects.nonNull(sort)) {
            Set<Map.Entry<String, Object>> entries = sort.entrySet();
            entries.forEach(entry -> {
                Object value = entry.getValue();
                int i = Integer.parseInt(String.valueOf(value));
                if (i > 0) {
                    solrQuery.addSort(entry.getKey(), SolrQuery.ORDER.asc);
                } else {
                    solrQuery.addSort(entry.getKey(), SolrQuery.ORDER.desc);
                }
            });
        }
    }

}
